package com.socketio;

import com.UnifiedBusiness2;
import com.alibaba.fastjson.JSONObject;
import com.annotation.SocketEvent;
import com.config.Config;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.SocketIOClient;
import com.util.ConfigUtil;
import com.util.StringUtils;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Ricky
 * @date 2020-9-4
 */
@Slf4j
public class SocketIoServeice {

    private final static Config CONFIG = ConfigUtil.getConfig();

    private final static Map<String, SocketIOClient> SOCKET_LIST = new HashMap<>();

    private static synchronized void addClient(SocketIOClient client) {
        SOCKET_LIST.put(client.getSessionId().toString(), client);
        InetSocketAddress address = client.getHandshakeData().getAddress();
        System.out.println("有客户端" + address.getHostString() + "进入，当前连接数：" + SOCKET_LIST.size());
    }

    private static synchronized void removeClient(SocketIOClient client) {
        SOCKET_LIST.remove(client.getSessionId().toString());
        InetSocketAddress address = client.getHandshakeData().getAddress();
        System.out.println("有客户端" + address.getHostString() + "断开，当前连接数：" + SOCKET_LIST.size());
    }

    public static void sendEvent(String socketId, String eventName, Object progress) {
        SocketIOClient client = SOCKET_LIST.get(socketId);
        if (client != null) {
            client.sendEvent(eventName, progress);
        }
    }

    public static void start() {
        com.corundumstudio.socketio.SocketIOServer server = getServer();
        server.addConnectListener(SocketIoServeice::addClient);
        Method[] declaredMethods = UnifiedBusiness2.class.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            SocketEvent annotation = declaredMethod.getAnnotation(SocketEvent.class);
            if (annotation == null) {
                continue;
            }
            String eventName = annotation.value();
            if (StringUtils.isEmpty(eventName)) {
                eventName = declaredMethod.getName();
            }

            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();

            server.addEventListener(eventName, parameterTypes[0], (client, data, ackSender) -> {

                if (!ackSender.isAckRequested()) {
                    new Thread(() -> {
                        try {
                            declaredMethod.invoke(null, data);
                        } catch (IllegalAccessException | InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }).start();
                    return;
                }
                Object resData;
                try {
                    resData = declaredMethod.invoke(null, data);
                } catch (Exception e) {
                    e.printStackTrace();
                    Throwable cause = e.getCause();
                    String message = e.getMessage();
                    if (cause != null) {
                        message = cause.getMessage();
                    }

                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("errmsg", message);

                    resData = jsonObject;
                }
                ackSender.sendAckData(resData);
            });
        }
        server.addEventListener("exit", Void.class, (client, data, ackSender) -> {
            if (!CONFIG.isDebugger()) {
                System.exit(0);
            }
        });
        server.addDisconnectListener(SocketIoServeice::removeClient);
        server.start();
        log.info("socketio已启动");
    }

    private static com.corundumstudio.socketio.SocketIOServer getServer() {
        Configuration config = new Configuration();
        config.setPort(9011);
        config.setRandomSession(true);
        return new com.corundumstudio.socketio.SocketIOServer(config);
    }
}
